package wechat

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"prod/pkg/env"
	"prod/pkg/log"
	"prod/pkg/request_key"
	"strconv"
	"time"
)

const (
	_qyWechatURL  = "https://qyapi.weixin.qq.com"
	_corpID       = "ww408a692c72ddf86e"
	_departmentID = "7"
	_agentID      = 1000018
	_corpSecret   = "ez3WBQ5cJUHnpt-J-RmMD1ltI0DWx9ysRpC9fEU2qOE"
	_getToken     = "/cgi-bin/gettoken"
	_sendMessage  = "/cgi-bin/message/send"

	// bugMessage
	bugMessage = `
### <font color="warning">%s-%s有bug了, 请及时处理!!!</font>
#### <font color="info">path:</font>
    %s
#### <font color="info">body:</font>
    %s
#### <font color="info">request_id:</font>
    %s
#### <font color="info">trace_log:</font>
    %s
`
)

type wechatToken struct {
	token      string
	expireTime time.Time
}

var globalToken wechatToken

func init() {
	globalToken = wechatToken{}
}

func FormatBugMessage(ctx context.Context, env env.Type, serviceName, path, body, stackTrace string) string {
	if len(body) > 65535 {
		// 企业微信规定超长无法发送
		body = body[0:60000]
	}
	return fmt.Sprintf(bugMessage, serviceName, env.GetEnvName(), path, body,
		request_key.GetRequestID(ctx), stackTrace)
}

// getToken query access token with the specified secret 企业微信api获取公司token
func getToken() (string, error) {
	if globalToken.token != "" && globalToken.expireTime.After(time.Now()) {
		return globalToken.token, nil
	}
	u := _qyWechatURL + _getToken
	req, err := http.NewRequest("GET", u, nil)
	if err != nil {
		return "", err
	}
	q := req.URL.Query()
	q.Add("corpid", _corpID)
	q.Add("corpsecret", _corpSecret)
	req.URL.RawQuery = q.Encode()
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return "", err
	}
	defer func() { _ = resp.Body.Close() }()
	res := struct {
		ErrCode     int           `json:"errcode"`
		ErrMsg      string        `json:"errmsg"`
		AccessToken string        `json:"access_token"`
		ExpiresIn   time.Duration `json:"expires_in"`
	}{}
	decErr := json.NewDecoder(resp.Body).Decode(&res)
	if decErr == io.EOF {
		decErr = nil // ignore EOF errors caused by empty response body
	}
	if decErr != nil {
		err = decErr
	}
	if res.ErrCode != 0 {
		return "", errors.New(res.ErrMsg)
	}
	globalToken.token = res.AccessToken
	globalToken.expireTime = time.Now().Add(time.Second * (res.ExpiresIn - 60))
	return res.AccessToken, nil
}

// AsyncSendMarkDown
func AsyncSendMarkDown(ctx context.Context, msg string) {
	reqID := request_key.GetRequestID(ctx)
	go func() {
		_ = sendMarkDown(request_key.GenerateContextWithRequestID(reqID), msg)
	}()
}

type wechatResponse struct {
	ErrorCode    int    `json:"errcode"`
	ErrorMessage string `json:"errmsg"`
}

// sendMarkDown 发送markdown消息
func sendMarkDown(ctx context.Context, msg string) (err error) {
	var (
		u        string
		req      *http.Request
		token    string
		bodyJSON []byte
		respByte []byte
		respBody wechatResponse
	)
	token, err = getToken()
	if err != nil {
		log.FromContext(ctx).Error(
			fmt.Sprintf("wechat markdown failed, err=%s", err.Error()))
		return err
	}
	if len(msg) > 65535 {
		// 企业微信规定超长无法发送
		msg = msg[0:65535]
	}
	msg = strconv.Quote(msg)
	bodyJSON = []byte(fmt.Sprintf(`{"toparty": "%s", "msgtype": "%s", "agentid": %d, "markdown": {"content": %s}}`, _departmentID, "markdown", _agentID, msg))
	u = _qyWechatURL + _sendMessage
	req, err = http.NewRequest("POST", u, bytes.NewBuffer(bodyJSON))
	if err != nil {
		log.FromContext(ctx).Error(
			fmt.Sprintf("wechat markdown failed, err=%s", err.Error()))
		return
	}
	q := req.URL.Query()
	q.Add("access_token", token)
	req.URL.RawQuery = q.Encode()
	req.Header.Set("Content-Type", "application/json")
	resp, err := http.DefaultClient.Do(req)
	defer func() { _ = resp.Body.Close() }()
	if err == nil {
		respByte, err = ioutil.ReadAll(resp.Body)
		if err == nil {
			err = json.Unmarshal(respByte, &respBody)
			if err == nil && respBody.ErrorCode != 0 {
				err = fmt.Errorf(string(respByte))
			}
		}
	}
	if err != nil {
		log.FromContext(ctx).Error(
			fmt.Sprintf("wechat markdown failed, err=%s", err.Error()))
		return
	}
	return
}
